#define _GNU_SOURCE
#include <sys/wait.h>
#include <sys/utsname.h>
#include <sched.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mount.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <limits.h>
/* A simple error-handling function: print an error message based
   on the value in 'errno' and terminate the calling process */

#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
//通过管道进行父子进程的通信
struct pipefd {
    int    pipe_fd[2];  /* Pipe used to synchronize parent and child */
};
//写入进程的 /proc/self/uid_map 和gid_map中
static void
update_map(char *mapping, char *map_file)
{
    int fd, j;
    size_t map_len;     /* Length of 'mapping' */

    /* Replace commas in mapping string with newlines */

    map_len = strlen(mapping);
    for (j = 0; j < map_len; j++)
        if (mapping[j] == ',')
            mapping[j] = '\n';

    fd = open(map_file, O_RDWR);
    if (fd == -1) {
        fprintf(stderr, "open %s\n", map_file);
        exit(EXIT_FAILURE);
    }

    if (write(fd, mapping, map_len) != map_len) {
        fprintf(stderr, "write %s\n", map_file);
        exit(EXIT_FAILURE);
    }

    close(fd);
}

//通过对ifreq获取信息并用ioctl对网卡进行配置
int setip(char *name,char *addr,char *netmask) {
    struct ifreq ifr;
    int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    ifr.ifr_addr.sa_family = AF_INET;
    inet_pton(AF_INET, addr, ifr.ifr_addr.sa_data + 2);
    ioctl(fd, SIOCSIFADDR, &ifr);
    inet_pton(AF_INET, netmask, ifr.ifr_addr.sa_data + 2);
    ioctl(fd, SIOCSIFNETMASK, &ifr);
    //get flags
    ioctl(fd, SIOCGIFFLAGS, &ifr);  
    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
    // set flags
    ioctl(fd, SIOCSIFFLAGS, &ifr);

    return 0;
}

static int              /* Start function for cloned child */
childFunc(void *arg)//通过参数的方式将管道传递给子进程
{
    struct utsname uts;
    struct pipefd *mypipe=(struct pipefd *)arg;
    close(mypipe->pipe_fd[1]);
    /* 改容器uts名称 */
    if (sethostname("mycontainer", 5) == -1)
        errExit("sethostname");
    if (uname(&uts) == -1)
        errExit("uname");
    printf("uts.nodename in child:  %s\n", uts.nodename);
    printf("real uid is %d\n", getuid());
    printf("effective uid is %d\n", geteuid());
     system("pwd");
     chroot("./container");//改变当前根目录
     chdir("/");
     setip("veth1","10.0.0.2","255.0.0.0");//配置容器网卡
     mount("proc", "/proc", "proc", 0, NULL);//mount文件系统
     execlp("/bin/sh", "/bin/sh" , NULL);
     printf("set done");
    return 0;           /* Terminates child */
}

#define STACK_SIZE (1024 * 1024)    /* Stack size for cloned child */

static char child_stack[STACK_SIZE];

int
main(int argc, char *argv[])
{
    pid_t child_pid;
    struct utsname uts;
    char buf[255];
    char *uid_map, *gid_map;
    uid_map="0 1000 1";//配置映射关系
    gid_map="0 1000 1";
    char map_path[PATH_MAX];
    struct pipefd mypipe;

    if (pipe(mypipe.pipe_fd) == -1)
        errExit("pipe");
    child_pid = clone(childFunc, 
                    child_stack + STACK_SIZE,   /* Points to start of 
                                                   downwardly growing stack */ 
                    CLONE_NEWUTS | CLONE_NEWCGROUP|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWNET|CLONE_NEWUSER|SIGCHLD, &mypipe);
    if (child_pid == -1)
        errExit("clone");
    printf("PID of child created by clone() is %ld\n", (long) child_pid);
    printf("set net \n");
    sprintf(buf,"sudo ip link add name veth0 type veth peer name veth1 netns %d",child_pid);
    system(buf);
    setip("veth0","10.0.0.1","255.0.0.0");
   
    snprintf(map_path, PATH_MAX, "/proc/%ld/uid_map",(long) child_pid);
    update_map(uid_map, map_path);
    //snprintf(map_path, PATH_MAX, "/proc/%ld/gid_map",(long) child_pid);
    //update_map(gid_map, map_path);

    close(mypipe.pipe_fd[1]);
    if (uname(&uts) == -1)
        errExit("uname");
    printf("uts.nodename in parent: %s\n", uts.nodename);

    if (waitpid(child_pid, NULL, 0) == -1)      /* Wait for child */
        errExit("waitpid");
    printf("child has terminated\n");

    exit(EXIT_SUCCESS);
}